#pragma once
#include <iostream>
#include <ctype.h>
#include "lexer.h"



Token::Token(int tag)
    : tag(tag) {

}

Token::~Token() {
    
}


std::string Token::toString() {
    std::string s;
    if (tag < 256) {
        s.push_back(tag);
    }
    else {
        s.append(mapping[tag - 256]);
    }
    return s;
}

const char* Token::mapping[] = { "AND", "BASIC", "BREAK", "DO", "ELSE", "EQ", "FALSE", "GE", "ID", "IF", "INDEX", "LE", "MINUS", "NE", "NUM", "OR", "REAL", "TEMP", "TRUE", "WHILE" };

NumToken::NumToken(int val)
    : Token(Token::NUM), value(val) {
}

Word::Word(TokenType tag, std::string s)
    : Token(tag), lexeme(s) {

}



Lexer::Lexer(const std::string& str)
    :m_buffer(str), m_idx(0) {
    std::cout << "Lexer init ..." << std::endl;
    reserve(new Word(Token::TokenType::IF, "if"));
    reserve(new Word(Token::TokenType::ELSE, "else"));
    reserve(new Word(Token::TokenType::WHILE, "while"));
    reserve(new Word(Token::TokenType::DO, "do"));
    reserve(new Word(Token::TokenType::BREAK, "break"));
    reserve(new Word(Token::TokenType::TRUE, "true"));
    reserve(new Word(Token::TokenType::FALSE, "false"));
}

Lexer::~Lexer() {
    for (Token* token : tokens) {
        delete token;
    }
}


const char* Token::getTagName() {
    return mapping[tag - 256];
}

void Lexer::reserve(Word *w) {
    tokens.insert((Token*)w);
    words[w->lexeme] = w;
}

Token* Lexer::scan() {
   
    while (m_buffer[m_idx] == ' ' || m_buffer[m_idx] == '\t' || m_buffer[m_idx] == '\r')
        ++m_idx;
    if (!m_buffer[m_idx])
        return nullptr;
    if (isdigit(m_buffer[m_idx])) {
        int value = 0;
        while (isdigit(m_buffer[m_idx])) {
            value = value*10 + m_buffer[m_idx] - '0';
            ++m_idx;
        }
        Token* token = (Token*) new NumToken(value);
        tokens.insert(token);
        return token;
    } else if (isalpha(m_buffer[m_idx])) {
        std::string word;
        while (isalpha(m_buffer[m_idx])) {
            word.push_back(m_buffer[m_idx]);
            ++m_idx;
        }
        Word *w;
        if (words.find(word) != words.end()) {
            w = words[word];
        } else {
            w = new Word(Token::TokenType::ID, word);
            tokens.insert((Token*)w);
        }
        return (Token*) w;
    }
    return new Token(m_buffer[m_idx++]);    
}

